/*
 * Copyright 2002-2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.jfast.framework.web.view;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.jfast.framework.base.util.Assert;
import cn.jfast.framework.log.LogFactory;
import cn.jfast.framework.log.LogType;
import cn.jfast.framework.log.Logger;
import cn.jfast.framework.base.cache.SDKProp;
import cn.jfast.framework.web.api.Api;
import cn.jfast.framework.web.api.ApiInvocation;
import cn.jfast.framework.web.core.ApplicationContext;
import cn.jfast.framework.web.view.viewtype.*;

public class ViewDriver {

    private Logger log = LogFactory.getLogger(LogType.Jfast, ViewDriver.class);

    private List<Exception> expList = new ArrayList<Exception>();

    public void render(Object view, HttpServletRequest request,
                       HttpServletResponse response, List<Exception> ex) {
        if (null == ex || ex.isEmpty()) {
            if(null != view) {
                if (view instanceof Jsp)
                    this.renderJSP((Jsp) view, request, response);
                else if (view instanceof Text)
                    this.renderText((Text) view, request, response);
                else if (view instanceof Json)
                    this.renderJSON((Json) view, request, response);
                else if (view instanceof Download)
                    this.renderDownload((Download) view, request, response);
                else if (view instanceof cn.jfast.framework.web.view.viewtype.Api)
                    this.renderService((cn.jfast.framework.web.view.viewtype.Api) view, request, response);
                else if (view instanceof String)
                    this.renderJSP((Jsp)view, request, response);
                else if (view instanceof Callback)
                    this.renderCallback((Callback)view, request, response);
                else
                    this.renderNull(view, request, response);
            }
        } else {
            try {
                for(Exception e:ex){
                    e.printStackTrace();
                }
                if (SDKProp.devMode) {
                    String expHtml = "";
                    response.reset();
                    response.setContentType("text/html;charset=" + SDKProp.encoding);
                    for(Exception e:ex){
                        expHtml += generateException(e);
                    }
                    response.getWriter().println(expHtml);
                }
            } catch (IOException e) {
                log.error(e.getMessage());
            }

        }
    }

    private void renderCallback(Callback view, HttpServletRequest request, HttpServletResponse response) {
        ObjectOutputStream os = null;
        try {
            response.reset();
            response.setCharacterEncoding(SDKProp.encoding);
            response.setContentType("application/x-java-serialized-object");
            os = new ObjectOutputStream(response.getOutputStream());
            os.writeObject(view.getView());
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void renderText(Text view, HttpServletRequest request, HttpServletResponse response) {
        try {
            response.reset();
            response.setCharacterEncoding(SDKProp.encoding);
            response.setContentType("text/plain; charset="
                    + SDKProp.encoding);
            response.getWriter().println(view.getView());
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            try {
                response.getWriter().close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void renderNull(Object view,
                            HttpServletRequest request, HttpServletResponse response) {

    }

    private void renderService(cn.jfast.framework.web.view.viewtype.Api view, HttpServletRequest request,
                               HttpServletResponse response) {
        for (Entry<String, String> requestAttr : view.getActionAttrs().entrySet()) {
            request.setAttribute(requestAttr.getKey(), requestAttr.getValue());
        }
        Api action = ApplicationContext.getApi(view.getView() + "/$" + request.getMethod().toLowerCase() + "$");
        Assert.notNull(action, "找不到对应API [" + view.getView() + "/$" + request.getMethod().toLowerCase() + "$]");
        try {
            new ApiInvocation(action, action.getApi().newInstance(), request, response, view.getView() + "/$" + request.getMethod().toLowerCase() + "$").invoke();
        } catch (InstantiationException e) {
            expList.add(e);
            this.render(view,request,response,expList);
        } catch (IllegalAccessException e) {
            expList.add(e);
            this.render(view,request,response,expList);
        }
    }

    private void renderDownload(Download view, HttpServletRequest request,
                                HttpServletResponse response) {
        response.reset();
        ServletOutputStream out = null;
        try {
            response.reset();
            response.setCharacterEncoding(SDKProp.encoding);
            response.setContentType("APPLICATION/OCTET-STREAM");
            String fileName = response.encodeURL(new String(view.getFilename().getBytes(), SDKProp.encoding));
            if (view.getMode() == DownloadMode.OPTION) {
                response.setContentType("application/x-msdownload");
                response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            } else {
                response.setContentType(view.getContentType());
                response.setHeader("Content-Disposition", "inline; filename=\"" + fileName + "\"");
            }
            response.addHeader("Content-Length", "" + view.getFileData().length);
            out = response.getOutputStream();
            out.write(view.getFileData());
            response.setStatus(HttpServletResponse.SC_OK);
            response.flushBuffer();
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage());
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        }
    }

    private void renderJSP(Jsp view, HttpServletRequest request,
                           HttpServletResponse response) {

        try {
            if (view.getRoute() == Route.REDIRECT) {
                response.reset();
                response.setCharacterEncoding(SDKProp.encoding);
                response.setContentType("text/html;charset=" + SDKProp.encoding);
                response.sendRedirect(request.getContextPath()
                        + "/" + view.getView());
            } else {
                for (Entry<String, Object> requestAttr : view.getRequestAttrs().entrySet()) {
                    request.setAttribute(requestAttr.getKey(), requestAttr.getValue());
                }
                request.getRequestDispatcher(
                        "/" + view.getView()).forward(
                        request, response);
            }
        } catch (ServletException e) {
            log.error(e.getMessage());
        } catch (IOException e) {
            log.error(e.getMessage());
        }

    }

    private void renderJSON(Json view, HttpServletRequest request,
                            HttpServletResponse response) {
        try {
            response.reset();
            response.setCharacterEncoding(SDKProp.encoding);
            response.setContentType("application/json; charset="
                    + SDKProp.encoding);
            response.getWriter().println(view.getView());
        } catch (IOException e) {
            log.error(e.getMessage());
        } finally {
            try {
                response.getWriter().close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    private String generateException(Exception ex) {
        Throwable t = ex.getCause();
        StringBuffer sbf = new StringBuffer("<p style='font-family:Micro Yahei;font-size:12px;padding-left:20px;color:red;'>");
        sbf.append(ex.toString() + "<br>");
        StackTraceElement[] exs = ex.getStackTrace();
        for (StackTraceElement es : exs) {
            sbf.append("at " + es.getClassName() + "." + es.getMethodName() + "(" + es.getFileName() + ":" + es.getLineNumber() + ")<br>");
        }
        if (null != t) {
            sbf.append("<b>Cause By</b> : " + t.toString() + "<br>");
            StackTraceElement[] txs = t.getStackTrace();
            for (StackTraceElement es : txs) {
                sbf.append("at " + es.getClassName() + "." + es.getMethodName() + "(" + es.getFileName() + ":" + es.getLineNumber() + ")<br>");
            }
            sbf.append("</p>");
        }
        return sbf.toString();
    }
}
